package com.springboot.demo.limit;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

import org.junit.Test;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;

/**
 * 限流demo
 *
 * @author supu
 * @date 2019-10-12 17:26
 **/
public class LimitDemo {
    @Test
    public void limitTest() {
        // 本地缓存、key （Long）表示当前时间秒、value （AtomicLong）表示请求计数器
        LoadingCache<Long, AtomicLong> counter = CacheBuilder.newBuilder().
                expireAfterAccess(2, TimeUnit.SECONDS).build(
                new CacheLoader<Long, AtomicLong>() {
                    @Override
                    public AtomicLong load(Long aLong) throws Exception {
                        return new AtomicLong(0);
                    }
                }
        );

        for (int i = 0; i < 15; i++) {
            // 同时开启15个线程访问
            new Thread(() -> requestLimit(counter)).start();
        }

        try {
            // 这里休眠是为了多线程全部执行完输出结果
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 请求限制，本方法加锁是为了控制缓存LoadingCache get数据时候的并发
     *
     * @param counter
     */
    private synchronized void requestLimit(LoadingCache<Long, AtomicLong> counter) {
        try {
            // 流量限制数量
            long limit = 10;
            // 当前秒数
            long currentSecond = System.currentTimeMillis() / 1000;
            if (counter.get(currentSecond).incrementAndGet() > limit) {
                // 超出每秒内允许访问10个的限制
                System.out.println("第 " + counter.get(currentSecond) + " 个请求超出上限，限流了");
                return;
            }

            System.out.println("第 " + counter.get(currentSecond) + " 个业务处理");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


}
